package pv;

/**
 * 哲学家进餐问题
 * <p>
 * 描述：有五个哲学家坐在圆桌前, 在哲学家之间各有一个筷子, 哲学家要有两根筷子才能吃饭 <br />
 * 给每个哲学家编号, 为 0 ~ 4, 第 i 个哲学家的左手边筷子编号是 0, 右手边筷子的编号是 (i + 1) % 5
 * </p>
 * <p>
 * 怎么解决 <b>死锁</b> 问题
 * </p>
 * <p>
 * 死锁的四个<b style="color: red">必要条件</b>
 * 1、互斥条件
 * 2、不可剥夺条件
 * 3、请求和保持条件
 * 4、循环等待条件
 * </p>
 * <p>
 * 破环循环等待条件 <br />
 * 第一种：每次只允许 4 个哲学家进餐, 这样, 就会确保至少有一个哲学家进餐成功 <br />
 * 第二种：奇数哲学家吃饭时先拿左手边的筷子, 再拿右手边的筷子; 偶数哲学家相反. 这样, 也会确保至少有一个哲学家进餐成功
 * </p>
 * <p>
 * 破环请求和保持条件 <br />
 * 只有当哲学家同时拿到两根筷子时, 才能进餐
 * </p>
 *
 * @author yezh
 * @date 2024/7/9 19:46
 */
public class Philosopher {

    public static void main(String[] args) {
        new SleepLoopThread(() -> eat03(0), 1, 5, "哲学家0").start();
        new SleepLoopThread(() -> eat03(1), 1, 5, "哲学家1").start();
        new SleepLoopThread(() -> eat03(2), 1, 5, "哲学家2").start();
        new SleepLoopThread(() -> eat03(3), 1, 5, "哲学家3").start();
        new SleepLoopThread(() -> eat03(4), 1, 5, "哲学家4").start();
    }


    private static final Semaphore[] CHOPSTICKS = new Semaphore[5];

    static {
        CHOPSTICKS[0] = new Semaphore(1, "筷子0");
        CHOPSTICKS[1] = new Semaphore(1, "筷子1");
        CHOPSTICKS[2] = new Semaphore(1, "筷子2");
        CHOPSTICKS[3] = new Semaphore(1, "筷子3");
        CHOPSTICKS[4] = new Semaphore(1, "筷子4");
    }

    private static final Semaphore MUTEX = new Semaphore(4, "MUTEX");

    private static void eat01(int i) {
        MUTEX.p();

        CHOPSTICKS[i].p();
        CHOPSTICKS[(i + 1) % 5].p();

        System.out.println("哲学家 " + i + " 开始进餐");

        CHOPSTICKS[i].v();
        CHOPSTICKS[(i + 1) % 5].v();

        MUTEX.v();
    }

    private static void eat02(int i) {
        if (i % 2 != 0) {
            // 左手边筷子
            CHOPSTICKS[i].p();
            // 右手边筷子
            CHOPSTICKS[(i + 1) % 5].p();
        } else {
            CHOPSTICKS[(i + 1) % 5].p();
            CHOPSTICKS[i].p();
        }

        System.out.println("哲学家 " + i + " 开始进餐");

        CHOPSTICKS[i].v();
        CHOPSTICKS[(i + 1) % 5].v();
    }

    private static final Semaphore MUTEX1 = new Semaphore(1, "MUTEX1");

    private static void eat03(int i) {
        // 只有当哲学家拿到两个筷子时才能进餐
        // 这里的实现更准确地说是哲学家拿筷子的动作互斥
        // 即使哲学家的左右筷子都可以用, 也可能被阻塞, 因此并不能保证哲学家能拿到两个筷子就能进餐
        MUTEX1.p();
        CHOPSTICKS[i].p();
        CHOPSTICKS[(i + 1) % 5].p();
        MUTEX1.v();

        System.out.println("哲学家 " + i + " 开始进餐");

        CHOPSTICKS[i].v();
        CHOPSTICKS[(i + 1) % 5].v();
    }

}
